<!DOCTYPE html>
<html lang="en">

<head>
	<meta charset="UTF-8">
	<title>ES6-String-Array</title>
</head>

<body>

	<script>
		var name = '张三';
		var height = 1.8;
		// 标签函数+模板字符串
		tagFn `他叫${name},身高${height}米。`;

		function tagFn(arr, v1, v2) {
			console.log(arr);
			console.log(v1);
			console.log(v2);
		}

		// repeat() --- 将目标重复N次，返回一个新的字符串
		var nameRepeat = name.repeat(3);
		console.log('repeat', nameRepeat);

		// includes() --- 判断字符串是否含有指定的子字符串，返回true\false，相对于indexOf(),返回值更加直观
		var nameIncludes = '大涛子';
		console.log(nameIncludes.includes('大'));
		console.log(nameIncludes.includes('ha'));

		// statsWith()\endsWith() --- 判断子字符串是否出现在目标字符串的开头/尾部位置

		// codePointAt() --- 处理4个字节的字符
		// 一个字符固定为2个字节，对于那些需要4个字节存储的字符，javascript会认为它是两个字符，此时它的字符长度length为2
		var str1 = '𠮷';
		console.log(str1.charAt(0)) //�
		// 十进制数：134071，这个数字抓换成16进制就是20bb7，对应的Unicode编码则是\u20bb7
		console.log(str1.codePointAt()) //134071

		// String.fromCodePoint()
		// 函数的参数是一个字符对应的码点，返回对应的字符
		var str2 = String.fromCodePoint(134071);
		console.log('fromCodePoint', str2); // fromCodePoint 𠮷

		// String.raw()
		// 返回字符串最原始的样貌，即使含有转移符，都可以直接输出
		console.log('raw:', String.raw `hello\nworld`);

		// Unicode 
		// 国际码、统一码

		/* NUMBER */

		// ES6中，isNaN被移植到了Number对象上，同样的还有isFinite、parseInt、parseFloat

		// Number.isNaN --- 判断传入的是否是非数值
		// 传统的isNaN会把非数值的参数转化成数值在进行判断，而Number.isNaN只对数值类型有效，非数值类型的参数一律返回false
		// 当返回false的时候，不一定就是一个数值，有可能是一个非数值类型的参数
		var num1 = Number.isNaN(1.2);
		var num2 = Number.isNaN(123);
		var num3 = Number.isNaN('aaa');
		console.log(num1); //false
		console.log(num2); //false
		console.log(num3); //false

		// Number.isFinite()
		// 检查一个数值是否非无穷
		console.log('finite:', Number.isFinite(1)); //true
		// Infinity --- Infinity是window对象下的一个常量，表示一个无穷数
		console.log('finite:', Number.isFinite(Infinity)); //false
		// 非数值一律返回false
		console.log('finite:', Number.isFinite('adn')); //false 

		// Number.parseInt()、Number.parseFloat() 
		// 解析一个字符串，并返回一个整数\浮点数

		// 总结：isNaN() isFinite() parseInt() parseFloat() 被移植到了Number对象下，目的是为了渐渐地减少全局性的函数，把全局函数合理地规划到其他对象下，渐渐实现语言的模块化

		// Number.isInteger() --- 判断是否是整数
		console.log('isInteger:', Number.isInteger(3)); // true
		console.log('isInteger:', Number.isInteger(3.1)); // false
		// 注意：在js内部对整数和浮点数采用一样的存储方式，因此小数点后如果都是0的浮点数，都会被认为是整数
		console.log('isInteger:', Number.isInteger(3.0)); // true
		console.log('isInteger:', Number.isInteger(3.00)); // true

		// Number.EPSILON() --- 定义一个极小的数值
		// 用来判断浮点数的计算误差，如果不超过Number.EPSILON的值，表示可以接受这样的误差
		console.log('epsilon:', Number.EPSILON); // 2.220446049250313e-16

		// Number.isSafeInteger() --- 安全整数
		// Number.MAX_SAFE_INTEGER/Number.MIN_SAFE_INTEGER --- ES6定义了两个常量来表示这个范围的最大值和最小值
		console.log('isSafeInteger:', Number.isSafeInteger(Number.MAX_SAFE_INTEGER)); // true
		console.log('isSafeInteger:', Number.isSafeInteger(Number.MAX_SAFE_INTEGER + 1)); // false

		/* MATH */

		// Math.trunc() --- 去除一个数的小数部分，返回整数部分
		console.log('trunc:', Math.trunc(3)); // 3
		console.log('trunc:', Math.trunc(3.1)); // 3
		console.log('trunc:', Math.trunc('a')); // NaN

		// Math.sign() --- 判断一个数到底是正数、负数还是零
		console.log('sign:', Math.sign(0)); // 0
		console.log('sign:', Math.sign(3)); // 1
		console.log('sign:', Math.sign(-3)); // -1
		console.log('sign:', Math.sign('a')); //NaN

		// Math.cbrt() --- 计算一个数的立方根
		console.log('cbrt:', Math.cbrt(27)); // 3
		console.log('cbrt:', Math.cbrt(8)); // 2

		// Math.acosh(x) 返回 x 的反双曲余弦。
		// Math.asinh(x) 返回 x 的反双曲正弦。
		// Math.atanh(x) 返回 x 的反双曲正切。
		// Math.clz32(x) 返回 x 的 32 位二进制整数表示形式的前导 0 的个数。
		// Math.sinh(x) 返回x的双曲正弦。
		// Math.cosh(x) 返回 x 的双曲余弦。
		// Math.expm1(x) 返回 eˆx - 1。
		// Math.fround(x) 返回 x 的单精度浮点数形式。
		// Math.hypot(...values) 返回所有参数的平方和的平方根。
		// Math.imul(x, y) 返回两个参数以 32 位整数形式相乘的结果。
		// Math.log1p(x) 返回 1 + x 的自然对数。
		// Math.log10(x) 返回以 10 为底的x的对数。
		// Math.log2(x) 返回以 2 为底的 x 的对数。
		// Math.tanh(x) 返回 x 的双曲正切。

		/* Array */

		// Array.of() --- 将一组值转换成数组
		console.log(Array.of(1, 2, 3)); // [1,2,3]

		// Array.from() --- 可以将类似数组的对象或者可遍历的对象转化成真正的数组
		console.log(Array.from('hello'));

		// find() --- 找出数组中符合条件的第一个元素
		let arrFind = [1, 2, 3, 4, 5];
		let resultFind = arrFind.find(function (value) {
			return value > 2; //3
			// return value>5;  //undefined
		});
		console.log(resultFind); // 3

		// findIndex() --- 返回符合条件的第一个数组成员的位置，否则返回-1
		let rFindIndex = arrFind.findIndex(function (value) {
			return value > 2; //2
			// return value > 5;  //-1
		});
		console.log(rFindIndex);

		// fill() --- 用指定的值填充到数组
		console.log([1, 2, 3].fill(4)); //[4,4,4]
		console.log([1, 2, 3].fill(100, 1, 2)); //[1, 100, 3]
		console.log([1, 2, 3].fill(100, 1, 3)); //[1, 100, 100]
		console.log([1, 2, 3].fill(100, 1, 4)); //[1, 100, 100]

		// entries() --- 对数组的键值对进行遍历，返回一个遍历器
		// keys() --- 对数组的索引进行遍历。返回一个遍历器
		// values() --- 对数组的元素进行遍历，返回一个遍历器

		for (let [i, v] of ['a', 'b', 'c'].entries()) {
			console.log(i, v);
		}
		//0 "a"
		//1 "b"
		//3 "c"

		for (let index of ['a', 'b', 'c'].keys()) {
			console.log(index);
		}
		//0
		//1
		//2

		for (let values of ['a', 'b', 'c'].values()) {
			console.log(values);
		}
		//a
		//b
		//c

		// 数组推导 --- 非标准

		// 传统方式
		var arrs = [1, 2, 3, 4];
		var newArrs = [];
		for (let i = 0; i < arrs.length; i++) {
			newArrs.push(arrs[i] * 2);
		}
		console.log(newArrs); // [2,4,6,8]

		// var newNewArrs = [for(i of arrs) i*2];
		// console.log(newNewArrs);

		/* Object */

		// 属性简写
		var myname = 'yangtao';
		var myage = '26';
		var person = {
			'myname': myname,
			'myage': myage
		}
		console.log(person); //{myname: "yangtao", myage: "26"}
		// ES6
		var newPerson = {
			myname,
			myage
		};
		console.log(newPerson); //{myname: "yangtao", myage: "26"}

		// 方法简写
		var method = {
			say: function () {
				console.log('这是传统的表示法');
			}
		};
		//ES6
		var newMethod = {
			say() {
				console.log('这是ES6的表示法');
			}
		};

		// 属性名可以是表达式
		var f = 'first';
		var n = 'Name';
		var s = 'say';
		var h = 'Hello';

		var people = {
			[f + n]: 'YangTao',
			[s + h]() {
				return '你好吗？';
			}
		};
		console.log(people.firstName); //YangTao
		console.log(people.sayHello()); // 你好吗？

		// Object.is() --- 比较两个值是否严格相等
		console.log(Object.is('123', 123)); //false
		console.log(Object.is(123, 123)); //true

		// Object.assign() --- 将源对象的属性赋值到目标对象上
		let target = {
			'a': 1
		};
		let origin = {
			'b': 2,
			'c': 3
		};
		Object.assign(target, origin);
		console.log(target); // {a: 1, b: 2, c: 3}

		// Object.getPrototypeOf() --- 获取一个对象的prototype属性
		// Object.setPrototypeOf() --- 设置一个对象的prototype属性

		function People() {}
		People.prototype = {
			say() {
				console.log('hello');
			}
		}
		let allen = new People();
		allen.say(); // hello
		console.log(Object.getPrototypeOf(allen)); //{say: ƒ}

		Object.setPrototypeOf(
			allen, {
				say() {
					console.log('hi')
				}
			}
		)
		allen.say(); //hi

		/* Function */

		// 传参
		// 设置默认值的参数一定要放在最后
		// 只有当传入的参数为undefined，才会触发默认值赋值
		// 函数的参数是默认声明的，声明过的变量，就不能用let或者const关键字再次声明，否则会报错的
		function personFn(name = 'yangtao', age = '24') {
			console.log(name, age);
		}
		personFn(); //yangtao 24
		personFn('Liqin', '20'); //Liqin 20

		// rest参数 --- ...values(values是个数组)，后面不能再跟其他参数
		function sum(result, ...values) {
			console.log(values);
			values.forEach(function (v, i) {
				result += v;
			});
			console.log(result);
		}

		var res = 0;
		sum(res, 1, 2, 3, 4, 5); //15

		// 扩展运算符 --- 结合数组使用，把数组的元素用逗号分隔开来，组成一个参数序列
		function sumFn(a, b) {
			console.log(a + b);
		}
		let arrr = [1, 2];
		sumFn(...arrr); //3

		// 箭头函数 --- 减少代码量
		var sumFnn = a => a; // 第一个a表示是传进去的参数，第二个a表示函数体
		console.log(sumFnn(1)); //1
		var sumFn1 = (a, b) => {
			return a + b
		}
		console.log(sumFn1(1, 2)); //3

		var sums1 = 0;
		[1, 2, 3, 4].forEach(v => sums1 += v);
		console.log(sums1); //10

		// 箭头函数的 this --- 指向定义时的this,并不是执行时的 this

		// ES5
		var myObj = {
			names: 'YangTao',
			show() {
				var that = this; //将执行时的this存储
				setTimeout(
					function () {
						// console.log(that.names);  //ES5可以使用此方法
						console.log(this.names); //this指向执行时的this，即window
					},
					500
				);
			}
		};
		myObj.show(); //undefined

		// ES6
		var myAge = {
			age: 26,
			show() {
				setTimeout(() => {
					console.log(this.age);
				}, 500);
			}
		}
		myAge.show();  //26
	</script>
</body>

</html>